package org.wak.character.display
{
	import fl.motion.easing.Linear;
	
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;
	
	import gs.TweenLite;
	
	import org.wak.action.actions.AdventureActions;
	import org.wak.baseoneonline.flash.geom.IntPoint;
	import org.wak.character.AdventureCharacter;
	import org.wak.scene.ISceneInteractable;
	import org.wak.scene.Scene;
	import org.wak.utils.Utils;


	/**
	* This class is the common interface for the graphical representation of a player
	* in a Scene. 
	*  
	*/
	public class AbstractCharacterDisplay extends Sprite implements ISceneInteractable
	{
		protected var mIsMoving:Boolean ;
		private var mPrevScreenPosition:Point = null ;
		protected var mMovingAngle:Number = 0;
		protected var mMovingTilesAngle:Number = 0;
		
		protected var mCurrentTile:IntPoint ;
		protected var mPrevTile:IntPoint ;
		
		// the id of this character display.
		protected var mDisplayId:String = "" ;
		
		// Walk index to walk to the points of the pathfinding route.
		// is controlled by the scene
		private var mWalkIndex:int = 0 ;
		
		// List of walking point created by the pathfinding .
		private var mWalkPoints:Array = null ; 
		
		// Goal point for the pathfinding is managed by the scene.
		private var mGoal:IntPoint = null ;
		// Starting point for the pathfinding is managed by the scene.
		private var mStart:IntPoint = null ;
		
		// The current scene where this display is.
		private var mCurrentScene:Scene = null ;
		
		// The player display walking speed.
		protected var mMoveSpeed:Number = 90 ;
		
		// Walk completed function.
		private var mOnCompleteFunc:Function = null ; 
		
		// The character who currently owns this display character.
		private var mOwnerCharacter:AdventureCharacter = null ;
		
		// Interaction point.
		private var mInteractionPoint:Point = null ;
		
		// anim behaviour that the player will have after walk.
		private var mInteractionPointBehaviour:String = "" ;
		
		// current scene id 
		private var mSceneId:String = "" ;
		
		// portrait DisplayObject
		protected var mPortrait:MovieClip = null ;
		
		protected var mEnabled:Boolean = true ;
		
		// the char display enable the perspective updates.
		protected var mUpdatePerspective:Boolean = true ; 
		
		/**
		* Constructor		
		*/
		public function AbstractCharacterDisplay()
		{
			super();
			mIsMoving = false ;
			
			mCurrentTile = new IntPoint();
			mPrevTile = new IntPoint();
			
			this.addEventListener(Event.ADDED_TO_STAGE,OnAddedToStage );
			mInteractionPoint = new Point();
		}
		
		/**
		 * Method to set the interaction point for this Character display. 
		 * @param pX horizontal coordinate of the interaction point.
		 * @param pY vertical coordinate of the interaction point.
		 * 
		 */		
		public function SetInteractionPoint( pX:Number, pY:Number ):void
		{
			mInteractionPoint.x = pX ;
			mInteractionPoint.y = pY ;
		}
		
		/**
		* Getter to know if the player is moving through the scene.		
		* 
		* @return true if the player is moving else false.		
		*/
		public function get isMoving():Boolean
		{
			return mIsMoving ;
		}
		
		// Auxiliary var to know if the method SetCurrentTile is called by first time.
		private var mAuxSetCurrentTileCalled:Boolean = false; 
		
		/**
		* This method is called by the Scene containing this PlayerRenderer
		* to set the current tile inside the background tile map.		
		* 
		* @param pX: current horizontal coordinate of the tile where the player		
		* @param pY: current vertical coordinate of the tile where the player
		*/
		public function SetCurrentTile(pX:int, pY:int):void
		{
			mPrevTile.x = mCurrentTile.x ;
			mPrevTile.y = mCurrentTile.y ;
			
			mCurrentTile.x = pX ;
			mCurrentTile.y = pY ;
			
			// the first time prev and current are the same
			if(!mAuxSetCurrentTileCalled)
			{
				mAuxSetCurrentTileCalled = true ;
				mPrevTile.x = pX ;
				mPrevTile.y = pY ;
			}
			
		}
		
		/**
		* Method to test if the param pTile is equal to the current tile where the player is.
		* 
		* @param pTile: IntPoint with the coordinates of a tile.		
		* @return true if pTile is equal to the current tile of the player.
		*/
		public function CurrentTileIsEqual( pTile:IntPoint ):Boolean
		{
			if(pTile == null)
				return false ;
			
			if( (pTile.x == mCurrentTile.x) && (pTile.y == mCurrentTile.y) )
			{
				return true ;
			}
			
			return false ;
		}
		
		/**
		 * Handler of the ADDED_TO_STAGE event.
		 */ 
		protected function OnAddedToStage(event:Event):void
		{
			this.removeEventListener(Event.ADDED_TO_STAGE,OnAddedToStage);
			this.addEventListener(Event.REMOVED_FROM_STAGE, OnRemovedFromStage ); 
			this.addEventListener(Event.ENTER_FRAME,Update);
			
			if( this.parent is Scene)
			{
				mCurrentScene = this.parent as Scene ;
			}
			
		}
		
		/**
		 * Handler of the REMOVED_FROM_STAGE event.
		 */ 
		protected function OnRemovedFromStage(event:Event):void
		{
			this.removeEventListener(Event.REMOVED_FROM_STAGE,OnRemovedFromStage);
			this.removeEventListener(Event.ENTER_FRAME, Update);
			
			mCurrentScene = null ;
			//mPrevScreenPosition = null ;
		}
		
		public function ResetPosition(pX:Number, pY:Number):void
		{
			this.x = pX ;
			this.y = pY ;
			
			mPrevScreenPosition.x = pX ;
			mPrevScreenPosition.y = pY ;
		}
		
		public function ResetTilePosition(pX:Number, pY:Number):void
		{
			//trace("ResetTilePosition: "+ pX + " "+ pY );
			mCurrentTile.x = pX ;
			mCurrentTile.y = pY ;
			
			mPrevTile.x = pX ;
			mPrevTile.y = pY ;
		}
		
		/**
		 * Handler of the ENTER_FRAME event. Main Loop of the AbstractPlayerRenderer.
		 */ 
		public function Update(event:Event):void
		{
			if(mPrevScreenPosition == null)
				mPrevScreenPosition = new Point(this.x, this.y);
			
			//var fTempTileAngleMov:Number = 0 ;
			
			if( (mPrevScreenPosition.x != this.x) || (mPrevScreenPosition.y != this.y) )
			{
				mIsMoving = true ;
				mMovingAngle = Utils.AngleInTwoPointsP( mPrevScreenPosition.x, mPrevScreenPosition.y, this.x,this.y );
				//trace("mMovingAngle:"+mMovingAngle);
				
				//fTempTileAngleMov = Utils.AngleInTwoPointsP(mPrevTile.x, mPrevTile.y,mCurrentTile.x,mCurrentTile.y );
				if( (mCurrentTile.x != mPrevTile.x) || (mCurrentTile.y != mPrevTile.y) )
				{
					//trace("tile changed prev:"+mPrevTile + " now "+ mCurrentTile );
					
					
					//trace("AngleInTwoPointsP: ("+mPrevTile.x+";"+ mPrevTile.y+")  ("+mCurrentTile.x+";"+mCurrentTile.y+")" );
					// Update the moving tile angle using prev and current tile
					mMovingTilesAngle = Utils.AngleInTwoPointsP(mPrevTile.x, mPrevTile.y,mCurrentTile.x,mCurrentTile.y );
					//mMovingTilesAngle = fTempTileAngleMov;
					//trace("mMovingTilesAngle:"+mMovingTilesAngle);
				}
			}
			else
			{
				mIsMoving = false ;
			}
			
			mPrevScreenPosition.x = this.x ;
			mPrevScreenPosition.y = this.y ;
			
			//if(mIsMoving)
			//trace("mIsMoving:"+mIsMoving+ "  mMovingAngle:"+ Math.round(Utils.ToDeg(mMovingAngle))+ " tileAng:"+ Math.round(Utils.ToDeg(mMovingTilesAngle) ));
						
		}
		
		
		/**
		 * The moving speed when movinh through the scene.
		 * Usually the walk speed. 
		 * @param value
		 * 
		 */		
		public function set moveSpeed(value:Number):void
		{
			mMoveSpeed = value; 
		}
		
		public function get moveSpeed():Number
		{
			return mMoveSpeed; 
		}
		
		
		/**
		 * Returns the deltaX of the screen coordinates.
		 */ 
		public function get deltaX():Number
		{
			if(mPrevScreenPosition == null)
				return 0 ;
			
			return (this.x - mPrevScreenPosition.x); 
		}
		
		/**
		 * Returns the deltaY of the screen coordinates.
		 */ 
		public function get deltaY():Number
		{
			if(mPrevScreenPosition == null)
				return 0 ;
				
			return (this.y - mPrevScreenPosition.y); 
		}
		
		public function get baseline():int
		{
			return this.y ;
		}
		
		public function get displayObject():DisplayObject
		{
			return this ;
		}
		
		public function get interactableId():String
		{
			return mOwnerCharacter.characterId ;
		}
		
		/**
		 * Set an animation behaviour and play it.  
		 * @param pAnimBehaviour the id of the anim behaviour.
		 * @param pPlays number of times to play the anim.
		 * @param pOnPlayed Callback function, called when the animation finished.
		 * 
		 */		
		public function SetAnimBehaviour(pAnimBehaviour:String, pPlays:int = 1, pOnPlayed:Function = null):void
		{
			
		}
		
		public function set walkIndex(value:int):void
		{
			mWalkIndex = value ;
		}
		
		public function get walkIndex():int
		{
			return mWalkIndex ;
		}
		
		public function set walkPoints(value:Array):void
		{
			mWalkPoints = value ;
		}
		
		public function get walkPoints():Array
		{
			return mWalkPoints ;			
		}
		
		public function set pfGoal(value:IntPoint):void
		{
			mGoal = value ;
		}
		
		public function get pfGoal():IntPoint
		{
			return mGoal ;
		}
		
		public function set pfStart(value:IntPoint):void
		{
			mStart = value ;
		}
		
		public function get pfStart():IntPoint
		{
			return mStart ;
		}
		
		public function get currentTile():IntPoint
		{
			return new IntPoint(mCurrentTile.x, mCurrentTile.y);
		}
		
		
		public function StartWalk(pOnCompleteHandler:Function = null):void
		{
			if(mWalkPoints.length == 0)
				return ;
			
			if(mWalkIndex == 0)
			{	
				//trace("assign!!!");
				mOnCompleteFunc = pOnCompleteHandler ;
				TweenLite.killTweensOf(this);
			}
			
				
			//trace("Start walk :mOnCompleteFunc:"+ mOnCompleteFunc + " wIx:"+mWalkIndex)
			
			var fNextPtTile:IntPoint = mWalkPoints[mWalkIndex] as IntPoint;		
			
			if((mCurrentTile.x != fNextPtTile.x) || (mCurrentTile.y != fNextPtTile.y))
				mMovingTilesAngle = Utils.AngleInTwoPointsP(mCurrentTile.x, mCurrentTile.y,fNextPtTile.x,fNextPtTile.y );
			//trace("StartWalk mMovingTilesAngle:"+ mMovingTilesAngle)
			ResetTilePosition(mCurrentTile.x, mCurrentTile.y);
			//trace("StartWalk:"+mMovingTilesAngle);
			//mPrevTile.x = fNextPtTile.x ;
			//mPrevTile.y = fNextPtTile.y ;
			
			var fNextPt:Point = mCurrentScene.mapToScreen(fNextPtTile);
			
			var fDistance:Number = Point.distance(new Point(this.x,this.y), fNextPt);
			var fTime:Number = fDistance / mMoveSpeed ;
			
			if( mWalkIndex < mWalkPoints.length - 1 )
			{
				//trace("c startWalk to :"+ fNextPtTile);
				TweenLite.to(this,fTime, {x: fNextPt.x, y:fNextPt.y, onComplete:StartWalk, ease: Linear.easeNone});
				
			}
			else
			{
				TweenLite.to(this,fTime, {x:fNextPt.x, y:fNextPt.y, onComplete:WalkDestinationReached, ease: Linear.easeNone});
				
			}
			
			mWalkIndex++ ;
		}
		
		public function StopWalk():void
		{
			TweenLite.killTweensOf(this);
		}
		
		public function WalkDestinationReached():void
		{
			//trace("WalkDestinationReached mOnCompleteFunc:"+mOnCompleteFunc);
			if(mOnCompleteFunc != null)
			{
				mOnCompleteFunc();
				//trace("assign2!!!");
				//mOnCompleteFunc = null ;
			}
			mCurrentScene.DetectGateSpotsCollision();
		}
		
		public function SetOwner( pOwner:AdventureCharacter ):void
		{
			mOwnerCharacter = pOwner ;
		}
		
		public function SetInteractionPointBehaviour(pB:String):void
		{
			mInteractionPointBehaviour = pB ;
		}
		
		public function get interactionBehaviour():String 
		{
			return mInteractionPointBehaviour ;
		}
		
		public function get interactionName():String
		{
			if(mOwnerCharacter == null)
				return "" ;
				
			return mOwnerCharacter.interactionName ;
		}
		
		public function get interactionPointX():Number
		{
			return mInteractionPoint.x ;
		}
		
		public function get interactionPointY():Number
		{
			return mInteractionPoint.y ;
		}
		
		public function get unitaryUse():Boolean 
		{
			return false ;
		}
		
		public function get description():String 
		{
			if(mOwnerCharacter != null )
				return mOwnerCharacter.description ;
				
			return "" ;
		}
		
		public function get sceneId():String
		{
			return mSceneId ;
		}
		
		public function set sceneId(value:String ):void
		{
			mSceneId = value;
		}
		
		public function get defaultAction():String 
		{
			return AdventureActions.ActionIdTalkTo ;
		}
		
		public function get stageX():Number
		{
			return 0 ;
		}
		
		public function get stageY():Number
		{
			return 0 ;
		}
		
		public function get dialogX():Number
		{
			return 0 ;
		}
		
		public function get dialogY():Number
		{
			return 0 ;
		}
		
		/**
		 * The current animation behaviour. 
		 * @return String with an anim behaviour id
		 * 
		 */		
		public function get currentBehaviour():String
		{
			return ""
		}
		
		/**
		 * Portrait display object of this CharacterDisplay 
		 * @return MovieClip
		 * 
		 */		
		public function get portrait():MovieClip
		{
			return mPortrait ;
		}
		
		public function get enabled():Boolean
		{
			return mEnabled ;
		}
		
		public function get perspectiveEnabled():Boolean
		{
			return mUpdatePerspective ;
		}
		
	}
}